#import "MuseekMessage.h"

@implementation MuseekMessage
@synthesize data;
-(NSMutableData *) data {
	if (data == nil) data = [[NSMutableData alloc] initWithCapacity:32];
	
	return data;
}

+(MuseekMessage *) messageWithData:(NSArray *)data order:(NSString *)order {
	MuseekMessage *m = [[MuseekMessage alloc] init];
	
	int i = 0;
	id param;
	
	for (param in data) {
		unichar opcode = [order characterAtIndex:i++];
		
		switch (opcode) {
			case 'i' : {
				[m appendUInt32:[param intValue]];
				break;
			}
				
			case 'b' : {
				[m appendByte:[param shortValue]];
				break;
			}
				
			case 's' : {
				[m appendString:param];
				break;
			}
				
			case 'l' : {
				[m appendUInt64:[param longValue]];
				break;
			}
		}
	}
	
	return [m autorelease];
}

-(MuseekMessage *) appendByte: (uint8_t) value {
	[self.data appendBytes:&value length:sizeof(uint8_t)];
	return self;
}

-(MuseekMessage *) appendUInt32: (uint32_t) value {
	uint32_t flippedValue = CFSwapInt32HostToLittle(value);
	[data appendBytes:&flippedValue length:sizeof(uint32_t)];
	return self;
}

-(MuseekMessage *) appendUInt64: (uint64_t) value {
	uint32_t flippedValue = CFSwapInt64HostToLittle(value);
	[self.data appendBytes:&flippedValue length:sizeof(uint64_t)];
	return self;
}

-(MuseekMessage *) appendData: (NSData *) value {
	[self.data appendData:value];
	return self;
}

-(MuseekMessage *) appendString: (NSString *) value {
	const uint8_t * charArray = (const uint8_t*)[value cStringUsingEncoding:NSUTF8StringEncoding];
	uint32_t stringLength = strlen((const char*)charArray);
	
	[self appendUInt32:stringLength];
	
	[self.data appendBytes: charArray 
						 length: stringLength];
	
	return self;
}


-(void *) bytes {
	NSAssert(data, @"Data is NULL. Did you call init first?");
	// updating the first 4 bytes - Message size
	NSRange r = NSMakeRange(0, sizeof(uint32_t));
	
	// remember, the message length DOES NOT count in total length
	uint32_t structSize = CFSwapInt32HostToLittle([data length] - sizeof(uint32_t));
	
	[data replaceBytesInRange:r withBytes: &structSize];
	
	return (void *)[data bytes];
}

-(long) length {
	if (!data) return 0;
	return [data length];
}

-(uint64_t) readUInt64 {
	if (pos >= [data length]) return 0;
	
	NSRange r = NSMakeRange(pos, sizeof(uint64_t));
	uint64_t value = 0;
	[data getBytes: &value 
					 range: r];
	pos += sizeof(uint64_t);
	
	return CFSwapInt64LittleToHost(value);
}

-(uint32_t) readUInt32 {
	if (pos >= [data length]) return 0;
  
	NSRange r = NSMakeRange(pos, sizeof(uint32_t));
	uint32_t value = 0;
	[data getBytes: &value range: r];
	pos += sizeof(uint32_t);
	
	return CFSwapInt32LittleToHost(value);
}

-(uint8_t) readByte {
	if (pos >= [data length]) return 0;
	
	NSRange r = NSMakeRange(pos, sizeof(uint8_t));
	uint8_t value = 0;
	[data getBytes:&value range:r];
	pos += sizeof(uint8_t);
	
	return value;
}

-(NSString *) readString {
	if (pos >= [data length]) return @"";
	
	uint32_t size = [self readUInt32];
	
	if (size + pos > [data length]) {
		NSLog(@"Weird string, longer than the Message... truncating");
		size = [data length] - pos;
	}
	
	NSRange r = NSMakeRange(pos, size);
	
	char *c = malloc(size + 1); // just for sure ;)
	c[size] = 0;
	[data getBytes:c range:r];
	NSString *result = [[NSString alloc] initWithBytes:c length:size encoding:NSUTF8StringEncoding];	
	pos += size;
	
	free(c);
	return [result autorelease];
}

-(void) setPos:(long) newPos {
	pos = newPos + 4;
	NSAssert (pos <= [data length], @"Trying set cursor beyond the data...");
}

-(long) pos {
	return pos - 4;
}

-(NSNumber *) code {
	int requiredLen = sizeof(uint32_t) * 2;
	
	if ([data length] < requiredLen) return [NSNumber numberWithInt:0];
	
	long oldPos = pos;
	pos = 4;
	
	NSNumber *result;
  result = [NSNumber numberWithInt:[self readUInt32]];
	
	pos = oldPos;
	return result;
}


-(id) init {
	if (self = [super init]) {
		pos = 0;
		data = [[NSMutableData alloc] initWithCapacity:32];
		[self appendUInt32:0]; // size
	}
	return self;
}
@end
